package gormplus

import (
	"errors"
	"gorm.io/gorm"
	"reflect"
)

type GormRepo[T any] struct {
	*gorm.DB
}

func (repo *GormRepo[T]) GetById(id any) (T, error) {
	var dest T
	k := reflect.TypeOf(dest).Kind()
	if k == reflect.Ptr {
		interf := repo.ptrToInterface(dest)
		err := repo.First(interf, id).Error
		return interf.(T), err

	} else if k == reflect.Struct {
		err := repo.First(&dest, id).Error
		return dest, err

	} else {
		return dest, errors.New("不支持的类型")
	}
}

func (repo *GormRepo[T]) DirectFind(conds ...any) ([]T, error) {
	slice := make([]T, 0)
	err := repo.Find(&slice, conds...).Error
	return slice, err
}

func (repo *GormRepo[T]) DirectFindWithPage(pageNo, pageSize int, conds ...any) ([]T, int64, error) {
	slice := make([]T, 0)
	var count int64
	var zero T

	skip := (pageNo - 1) * pageSize
	err := repo.Model(zero).Where(conds[0], conds[1:]...).Count(&count).Offset(skip).Limit(pageSize).Find(&slice).Error
	return slice, count, err
}

func (repo *GormRepo[T]) OptionFind(cond T) ([]T, error) {
	slice := make([]T, 0)
	err := repo.Where(cond).Find(&slice).Error
	return slice, err
}

func (repo *GormRepo[T]) OptionFindWithPage(pageNo, pageSize int, cond T) ([]T, int64, error) {
	slice := make([]T, 0)
	var count int64

	var zero T
	skip := (pageNo - 1) * pageSize
	err := repo.Model(zero).Where(cond).Count(&count).Offset(skip).Limit(pageSize).Find(&slice).Error
	return slice, count, err
}

func (repo *GormRepo[T]) WhereIn(query string, arg any) *GormRepo[T] {
	k := reflect.TypeOf(arg).Kind()
	if k == reflect.Slice {
		val := reflect.ValueOf(arg)
		if val.Kind() == reflect.Slice && val.Len() > 0 {
			repo.DB = repo.Where(query, arg)
		}
	}
	return repo
}

func (repo *GormRepo[T]) WhereLike(query string, arg string) *GormRepo[T] {
	if len(arg) > 0 {
		copyRepo := GormRepo[T]{
			DB: repo.Where(query+" like ?", "%"+arg+"%"),
		}
		return &copyRepo
	}
	return repo
}

func (repo *GormRepo[T]) WhereEqual(query string, arg any) *GormRepo[T] {
	switch arg.(type) {
	case int:
		if arg.(int) != 0 {
			copyRepo := GormRepo[T]{
				DB: repo.Where(query+"= ?", arg),
			}
			return &copyRepo
		}
	case string:
		if arg.(string) != "" {
			copyRepo := GormRepo[T]{
				DB: repo.Where(query+"= ?", arg),
			}
			return &copyRepo
		}
	case *bool:
		if arg.(*bool) != nil {
			copyRepo := GormRepo[T]{
				DB: repo.Where(query+"= ?", arg),
			}
			return &copyRepo
		}
	}
	return repo
}

func (repo *GormRepo[T]) ptrToInterface(dest T) any {
	t := reflect.TypeOf(dest)
	t = t.Elem()
	value := reflect.New(t)
	return value.Interface()
}
